package com.anydata.android.widget.weatherclock;

import java.util.LinkedList;
import java.util.Queue;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.IBinder;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Log;
import android.widget.RemoteViews;

import com.anydata.android.weatherforecast.weather.WeatherSet;
import com.anydata.android.weatherforecast.weather.webservice.ParseException;
import com.anydata.android.weatherforecast.weather.webservice.WeatherForecastService;

/**
 * Background service to build any requested widget updates. Uses a single
 * background thread to walk through an update queue, querying
 * {@link WebserviceHelper} as needed to fill database. Also handles scheduling
 * of future updates, usually in 6-hour increments.
 */
public class UpdateService extends Service implements Runnable {
	private static final String TAG = "UpdateService";

	private static final String[] PROJECTION_APPWIDGETS = new String[] {
			"lastUpdated", "configured", };

	private static final int COL_CONFIGURED = 0;
	private static final int COL_LAST_UPDATED = 1;
	
	private static String select_clock_model = ConstData.DEFAULT_CLOCK_MODEL;

	/**
	 * Interval to wait between background widget updates. Every 6 hours is
	 * plenty to keep background data usage low and still provide fresh data.
	 */
	// private static final String UPDATE_INTERVAL = "30000";

	/**
	 * When rounding updates to the nearest-top-of-hour, trigger the update
	 * slightly early by this amount. This makes sure that we're already updated
	 * when the user's 6AM alarm clock goes off.
	 */
	private static final long UPDATE_TRIGGER_EARLY = 10 * DateUtils.MINUTE_IN_MILLIS;

	/**
	 * If we calculated an update too quickly in the future, wait this interval
	 * and try rescheduling.
	 */
	private static final long UPDATE_THROTTLE = 30 * DateUtils.MINUTE_IN_MILLIS;

	/**
	 * Specific {@link Intent#setAction(String)} used when performing a full
	 * update of all widgets, usually when an update alarm goes off.
	 */
	public static final String ACTION_UPDATE_ALL = "com.anydata.android.widget.UPDATE_ALL";

	/**
	 * Length of time before we consider cached forecasts stale. If a widget
	 * update is requested, and {@link AppWidgetsColumns#LAST_UPDATED} is inside
	 * this threshold, we use the cached forecast data to build the update.
	 * Otherwise, we first trigger an update through {@link WebserviceHelper}.
	 */
	private static final long FORECAST_CACHE_THROTTLE = 3 * DateUtils.HOUR_IN_MILLIS;

	/**
	 * Number of days into the future to request forecasts for.
	 */
	private static final int FORECAST_DAYS = 4;

	/**
	 * Lock used when maintaining queue of requested updates.
	 */
	private static Object sLock = new Object();

	/**
	 * Flag if there is an update thread already running. We only launch a new
	 * thread if one isn't already running.
	 */
	private static boolean sThreadRunning = false;

	/**
	 * Internal queue of requested widget updates. You <b>must</b> access
	 * through {@link #requestUpdate(int[])} or {@link #getNextUpdate()} to make
	 * sure your access is correctly synchronized.
	 */
	private static Queue<Integer> sAppWidgetIds = new LinkedList<Integer>();

	public static final Uri CONTENT_URI = Uri.parse("content://"
			+ "com.anydata.android" + "/appwidgets");

	/**
	 * Request updates for the given widgets. Will only queue them up, you are
	 * still responsible for starting a processing thread if needed, usually by
	 * starting the parent service.
	 */
	public static void requestUpdate(int[] appWidgetIds) {
		synchronized (sLock) {
			for (int appWidgetId : appWidgetIds) {
				sAppWidgetIds.add(appWidgetId);
			}
		}
	}
	
	public static void setClockModel(String model){
		select_clock_model = model;
		
	}

	/**
	 * Peek if we have more updates to perform. This method is special because
	 * it assumes you're calling from the update thread, and that you will
	 * terminate if no updates remain. (It atomically resets
	 * {@link #sThreadRunning} when none remain to prevent race conditions.)
	 */
	private static boolean hasMoreUpdates() {
		synchronized (sLock) {
			boolean hasMore = !sAppWidgetIds.isEmpty();
			if (!hasMore) {
				sThreadRunning = false;
			}
			return hasMore;
		}
	}

	/**
	 * Poll the next widget update in the queue.
	 */
	private static int getNextUpdate() {
		synchronized (sLock) {
			if (sAppWidgetIds.peek() == null) {
				return AppWidgetManager.INVALID_APPWIDGET_ID;
			} else {
				return sAppWidgetIds.poll();
			}
		}
	}

	/**
	 * Start this service, creating a background processing thread, if not
	 * already running. If started with {@link #ACTION_UPDATE_ALL}, will
	 * automatically add all widgets to the requested update queue.
	 */
	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);

		AppWidgetManager manager = AppWidgetManager.getInstance(this);

		// If requested, trigger update of all widgets
		if (ACTION_UPDATE_ALL.equals(intent.getAction())) {
			Log.d(TAG, "Requested UPDATE_ALL action");
			// AppWidgetManager manager = AppWidgetManager.getInstance(this);
			requestUpdate(manager.getAppWidgetIds(new ComponentName(this,
					WeatherForecastAppWidget.class)));

		}

		// Only start processing thread if not already running
		synchronized (sLock) {
			if (!sThreadRunning) {
				sThreadRunning = true;
				new Thread(this).start();
			}
		}
	}


	/**
	 * Main thread for running through any requested widget updates until none
	 * remain. Also sets alarm to perform next update.
	 */
	public void run() {
		Log.d(TAG, "Processing thread started");

		AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
		ContentResolver resolver = getContentResolver();

		long now = System.currentTimeMillis();
		RemoteViews updateViews = null;

		while (hasMoreUpdates()) {
			int appWidgetId = getNextUpdate();
			WeatherForecastService wsService = new WeatherForecastService();
			SharedPreferences config = getSharedPreferences(
					ConstData.PREF_FILE_NAME, 0);
			String city_name = config.getString(ConstData.PREFS_CITY_FIELD_Key,
					ConstData.PREFS_CITY_KEY_DEFAULT);
//			String city_code = ConstData.getCityCodeByIndex(city_index);

			Log.d(TAG, "city_name is " + city_name);
//			Log.d(TAG, "City_code is " + city_code);

			WeatherSet ws = null;
			try {
				ws = wsService.getForecasts(city_name);
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (null != ws) {
				updateViews = WeatherForecastAppWidget
						.updateAppWidget(this, ws);
			}

			if (null != updateViews) {
				appWidgetManager.updateAppWidget(appWidgetId, updateViews);
			}

		}

		// Schedule next update alarm

		final SharedPreferences config = this.getSharedPreferences(
				ConstData.PREF_FILE_NAME, 0);
		String interval = config.getString(ConstData.UPDATE_INTERVAL,
				ConstData.DEFAULT_UPDATE_INTERVAL);
		Log.d(TAG, "The frequency = " + interval);

		Time time = new Time();
		long nowMillis = System.currentTimeMillis();
		time.set(nowMillis + Long.parseLong(interval) * 60 * 1000);
		long updateTimes = time.toMillis(true);
		Log.d(TAG, "request next update at " + updateTimes);
		
		//Settings.System.TIME_12_24

		Intent updateIntent = new Intent();
		updateIntent.setClass(this, UpdateService.class);

		PendingIntent pendingIntent = PendingIntent.getService(this, 0,
				updateIntent, 0);

		// Schedule alarm, and force the device awake for this update
		AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
		alarmManager.set(AlarmManager.RTC_WAKEUP, updateTimes, pendingIntent);

		// No updates remaining, so stop service
		stopSelf();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
}
